
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "nuclei_sdk_soc.h"
#include "dbg_cli.h"
#include "ethqos.h"




/*
 *  incr = 0, get curr postion.
 *  incr > 0, alloc.
 *  ince < 0, free.
 */
extern char __heap_start[];
void * _sbrk( intptr_t incr )
{
	static intptr_t  heapos = (intptr_t)__heap_start;
	void * prepos = (void *)heapos;
	
	if ( incr > 0 )  {
		/* judge range, err for return (void *)(-1).. */
		heapos += incr;
	} else {
		heapos += incr;
	}
	
	/**/
	return prepos;
}



int dbg_test_cycle( void * parg, int argc, const char * argv[] )
{
	uint32_t temp;
	uint32_t told;
	uint32_t tnew;
	dw_timer_regs_t * ptmr = (dw_timer_regs_t *)TIMER0_BASE;


	__enable_mcycle_counter();
	

	/* timer config */	
	ptmr->TCR = 0;
	ptmr->TLC = 100000000ul;
	ptmr->TCR = 2;
	ptmr->TCR = 3;
	told = __RV_CSR_READ( CSR_MCYCLE );

	while ( true )  {
		temp = ptmr->TIS;
		if ( temp != 0 )  {
			tnew = __RV_CSR_READ( CSR_MCYCLE );
			ptmr->TCR = 0;
			temp = ptmr->TEOI;
			break;
		}
	}
	
	temp = tnew - told;
	printf( "cycle : %lu\n", temp );
	return 0;
}


/*
使用 uart-1 的 de 信号输出 toggle 波形.
*/
int dbg_test_toggle( void * parg, int argc, const char * argv[] )
{
	dw_uart_regs_t * puart = (dw_uart_regs_t *)UART1_BASE;

	/* pinmux */
	// REG32( 0x2000F4 ) = 0xFFF00;


	puart->TCR = 0x1;
	
	for ( int i=0; i<100; i++ )  {

		puart->DE_EN = 0x0;
		puart->RE_EN = 0x1;
		__NOP();
		__NOP();
		__NOP();

		puart->DE_EN = 0x1;
		puart->RE_EN = 0x0;
		__NOP();
		__NOP();
		__NOP();

	}
	
	return 0;
}


int dbg_test_mcfg( void * parg, int argc, const char * argv[] )
{
	uint32_t temp;
	
	temp = __RV_CSR_READ( CSR_MCFG_INFO );
	printf( "mcfg = %lx\n", temp );
	
	temp = __RV_CSR_READ( CSR_MICFG_INFO );
	printf( "micfg = %lx\n", temp );

	temp = __RV_CSR_READ( CSR_MDCFG_INFO );
	printf( "mdcfg = %lx\n", temp );
	
	temp = __RV_CSR_READ( CSR_MILM_CTL );
	printf( "milm = %lx\n", temp );

	temp = __RV_CSR_READ( CSR_MDLM_CTL );
	printf( "mdlm = %lx\n", temp );
	return 0;
}


int qspi_single_cmd( dw_qspi_regs_t * pqspi, uint8_t cmd )
{
	uint32_t temp;

	// TMOD=1 (Write only), DFS=7
	pqspi->CTRLR0 = 0x707;
	pqspi->TXFTLR = 0;

	//
	pqspi->SSIENR = 1;
	pqspi->DR[0] = cmd;
	pqspi->SER = 1;

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x1) == 0 )  {
			break;
		}
	}

	pqspi->SER = 0;
	pqspi->SSIENR = 0;
	return 0;
}


int qspi_read( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	uint32_t temp;
	uint8_t tary[256];

	// disable
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 6;

	// TMOD=3 (EEPROM Read mode), DFS=7
	pqspi->CTRLR0 = 0xF07;
	pqspi->TXFTLR = 0x30000;

	// read 256 bytes.
	pqspi->CTRLR1 = 255;
	pqspi->SSIENR = 1;

	// cmd=0x3
	pqspi->DR[0] = 0x3;
	pqspi->DR[1] = (addr >> 16) & 0xFF;
	pqspi->DR[2] = (addr >> 8) & 0xFF;
	pqspi->DR[3] = addr & 0xFF;
	pqspi->SER = 1;

	// 
	for ( int i=0; i<256; i++ )  {

		while ( true )  {
			temp = pqspi->RXFLR;
			if ( temp > 0 )  {
				break;
			}
		}

		tary[i] = (uint8_t)( pqspi->DR[0] );
	}

	// dump...
	debug_dump_hex( tary, 256 );
	return 0;
}


int qspi_erase( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	return 0;
}


int qspi_program( dw_qspi_regs_t * pqspi, uint32_t addr )
{
	uint32_t temp;

	// disable
	pqspi->SSIENR = 0;
	pqspi->SER = 0;
	pqspi->BAUDR = 6;

	//
	qspi_single_cmd( pqspi, 0x6 );
	
	//
	pqspi->SSIENR = 0;
	pqspi->SER = 0;

	// TMOD=1 (Write only), DFS=7
	pqspi->CTRLR0 = 0x707;
	pqspi->TXFTLR = 0x30000;

	// cmd=0x3
	pqspi->SSIENR = 1;
	pqspi->DR[0] = 0x2;
	pqspi->DR[1] = (addr >> 16) & 0xFF;
	pqspi->DR[2] = (addr >> 8) & 0xFF;
	pqspi->DR[3] = addr & 0xFF;
	pqspi->SER = 1;

	// write incr data
	for ( int i=0; i<32; i++ )  {
		
		while ( true )  {
			temp = pqspi->TXFLR;
			if ( temp <= 8 )  {
				break;
			}
		}

		pqspi->DR[0] = (uint32_t)(i*8 + 0);
		pqspi->DR[1] = (uint32_t)(i*8 + 1);
		pqspi->DR[2] = (uint32_t)(i*8 + 2);
		pqspi->DR[3] = (uint32_t)(i*8 + 3);

		pqspi->DR[4] = (uint32_t)(i*8 + 4);
		pqspi->DR[5] = (uint32_t)(i*8 + 5);
		pqspi->DR[6] = (uint32_t)(i*8 + 6);
		pqspi->DR[7] = (uint32_t)(i*8 + 7);
	}

	// check busy..
	while ( true )  {
		temp = pqspi->SR;
		if ( (temp & 0x1) == 0 )  {
			break;
		}
	}

	//
	pqspi->SER = 0;	
	pqspi->SSIENR = 0;
	return 0;
}



/*
nor flash xip :

CTRLR0, CTRLR1, 
BAUDR, IMR, SER, 
SPI_CTRLR0(Dual/Quad/Octal), 
XIP_WRAP_INST, XIP_INCR_INST, XIP_CNT_TIME_OUT

a) 配置 standard spi 模式, 擦除和编程两种功能.
b) 配置 quad xip 模式, 能够读出来内容?
c) 配置 quad xip 模式, 测试没有指令的模式.
d) 配置 quad xip 模式, 测试 cache 使能之后的 wrap 读操作.

*/

int qspi_test( void * parg, int argc, const char * argv[] )
{
	int iret;
	uint32_t temp;
	dw_qspi_regs_t * pqspi = (dw_qspi_regs_t *)0x2000000;

	if ( argc < 3 )  {
		goto usage;
	}

	if ( 0 == strcmp( argv[1], "xip" ) )  {
		
		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse value fail = %d\n", iret );
			goto usage;
		}

		if ( temp == 0 )  {
			// todo : cache disable...
			// xip_en = 0;  sysctl + 0x10;
			REG32( 0x200000 + 0x10 ) = 0x4;

			// ssi enable...
			pqspi->SSIENR = 0;

		} else {

			// ssi enable...
			pqspi->SSIENR = 1;

			//  xip_en = 1;
			REG32( 0x200000 + 0x10 ) = 0x5;
		}

	} else if ( 0 == strcmp( argv[1], "erase" ) ) {

		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse addr fail = %d\n", iret );
			goto usage;
		}

		//
		iret = qspi_erase( pqspi, temp );
		

	} else if ( 0 == strcmp( argv[1], "write" ) ) {

		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse addr fail = %d\n", iret );
			goto usage;
		}

		//
		iret = qspi_program( pqspi, temp );

	} else if ( 0 == strcmp( argv[1], "read" ) ) {
		
		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )  {
			printf( "parse addr fail = %d\n", iret );
			goto usage;
		}

		iret = qspi_read( pqspi, temp );

	} else  {
		goto usage;
	}
	
	//
	printf( "action ret = %d\n", iret );
	return 0;
	
usage:
	printf( "usage:\n" );
	printf( "\t%s xip <0/1>\n", argv[0] );
	printf( "\t%s erase <addr>\n", argv[0] );
	printf( "\t%s write <addr>\n", argv[0] );
	printf( "\t%s read <addr>\n", argv[0] );
	return 0;
}




int main( void )
{
	int iret;
	char spad[128];

	printf( "hello...\n" );
	debug_cli_init();
	debug_add_cmd( "cycle", dbg_test_cycle,  NULL );
	debug_add_cmd( "toggle", dbg_test_toggle,  NULL );
	debug_add_cmd( "mcfg", dbg_test_mcfg,  NULL );
	debug_add_cmd( "qspi", qspi_test,  NULL );
	debug_add_cmd( "mdio", dbg_eth_mdio,  NULL );
	debug_add_cmd( "ephy", dbg_eth_phy,  NULL );
	debug_add_cmd( "emac", dbg_eth_mac,  NULL );
	debug_add_cmd( "echeck", dbg_eth_check,  NULL );
	

	/**/
	while ( true )  {

		/* read line */
		iret = cons_readline( ">> ", 120, spad );
		if ( iret != 0 ) {
			continue;
		}
			
		if ( 0 == strcmp( spad, "quit" ) )  {
			break;
		}

		/* proc */
		debug_proc_line( spad );
	}

	/**/
	return 0;
}

